1

webpack 提供了 webpack-dev-server 可以实现高效的页面自动刷新,但是这个 server 有一点局限性:

  • 虽然可以做为 server 中间件使用,但本身不提供其它资源的静态服务

  • 对于打包文件以外的变化它是无知的,也就无法去刷新

  • 如果是自己的页面需要引入 script,修改配置 entry,自己另外提供文件服务,步骤繁琐

现在假设我们有自己的 html 页面,页面引入了本地的 css,但是 js 是由 webpack 打包的,需要实现文件修改自动刷新的页面的话我们需要以下几个模块:

  • 文件监视 - gulp.watch()

  • 静态资源服务 - gulp-serve 模块

  • livereload服务 (负责接受变化请求并通知页面刷新) - gulp-livereload 模块

  • 自动添加 livereload 前端脚本 - connect-livereload 模块 (或者使用livereload浏览器插件)

代码如下:

// npm i gulp-serve gulp-livereload webpack gulp-util gulp connect-livereload -D
var serve = require('gulp-serve')
var livereload = require('gulp-livereload')
var webpack = require('webpack')
var gutil = require('gulp-util')
var gulp = require('gulp')
var webpackConfig = require('./webpack.config.js')
var inject = require('connect-livereload')()
var path = require('path')
var myConfig = Object.create(webpackConfig)
// for debugging
myConfig.devtool = 'sourcemap'
myConfig.debug = true

var paths = {
  scripts: ['index.js'],
  // file list for watching
  asserts: ['*.css', 'index.html']
}

gulp.task('default', ['build-dev'])

gulp.task('build-dev', ['webpack:build-dev', 'serve'], function () {
  livereload.listen({
    start: true
  })
  // build js files on change
  gulp.watch(paths.scripts, ['webpack:build-dev'])
  var watcher = gulp.watch(paths.asserts)
  watcher.on('change', function (e) {
    livereload.changed(e.path)
  })
})

// static server
gulp.task('serve', serve({
  root: [__dirname],
  // inject livereload script ot html
  middleware: inject
}))

var devCompiler = webpack(myConfig)
var outputFile = path.resolve(myConfig.output.path, myConfig.output.filename)

gulp.task('webpack:build-dev', function (callback) {
  devCompiler.run(function (err, stats) {
    if (err) throw new gutil.pluginError('webpack:build-dev', err) //eslint-disable-line
    gutil.log('[webpack:build-dev]', stats.toString({
      colors: true
    }))
    livereload.changed(outputFile)
    callback()
  })
})

这种做法最大的好处就是 html 和 css 不用都让 webpack 去处理也能做到自动刷新,但是相比 webpack-dev-server, 它是JS全部重新打包的,所以性能会差很多,此时我们可以使用 webpack-dev-server 实现打包文件的监听,同时让原来后台提供其它文件服务。假设后台使用 3000 端口,我们使用 webpack-dev-server inline模式需要如下配置:

gulp.task('webpack:dev-server', function () {
  var devServerConfig = Object.create(myConfig)
  // webpack need this to send request to webpack-dev-server
  devServerConfig.output.publicPath = 'http://localhost:8080/'
  devServerConfig.plugins = devServerConfig.plugins || []
  devServerConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
  // inline mode
  devServerConfig.entry.unshift('webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server')
  var compiler = webpack(devServerConfig)
  new WebpackDevServer(compiler, {
    contentBase: 'http://localhost:3000/',
    // Set this as true if you want to access dev server from arbitrary url.
    // This is handy if you are using a html5 router.
    historyApiFallback: false,
    // Don't forget this for dev-server
    publicPath: '/build/',
    lazy: false,
    hot: true
  }).listen(8080, 'localhost', function (err) {
    if (err) throw new gutil.PluginError('webpack-dev-server', err)
    // Server listening
    gutil.log('[webpack-dev-server]', 'http://localhost:8080/')
  })
})

gulp webpack:dev-server 现在可以启动服务了,但同时请求 bundle.js script也要修改为:

<script src="http://localhost:8080/bundle.js" type="text/javascript" charset="utf-8"></script>

这样 webpack-dev-server 才能进行热加载。

还有一种看起来更好的方式是使用 proxy 来代理,此时你访问 webpack-dev-server时它无法处理的请求都会转发给你的后台服务,配置起来相比上面要简单一些:

  • html 页面无需修改

  • config.output.publicPath 使用原来的相对路径

  • webpack-dev-server 配置 proxy 选项

gulp.task('webpack:dev-server', function () {
  var devServerConfig = Object.create(myConfig)
  // webpack need this to send request to webpack-dev-server
  devServerConfig.plugins = devServerConfig.plugins || []
  devServerConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
  // inline mode
  devServerConfig.entry.unshift('webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server')
  var compiler = webpack(devServerConfig)
  new WebpackDevServer(compiler, {
    // contentBase: {target: 'http://localhost:3000/'},
    // Set this as true if you want to access dev server from arbitrary url.
    // This is handy if you are using a html5 router.
    historyApiFallback: false,
    proxy: {
      '*': 'http://localhost:3000'
    },
    publicPath: '/build/',
    lazy: false,
    hot: true
  }).listen(8080, 'localhost', function (err) {
    if (err) throw new gutil.PluginError('webpack-dev-server', err)
    // Server listening
    gutil.log('[webpack-dev-server]', 'http://localhost:8080/')
  })
})

这么做只需要访问 webpack-dev-server 就可以直接访问页面,其它设备通过 ip 也能直接访问到,免去了修改的麻烦。其实 webpack-dev-server 如果可以直接添加静态文件服务的功能,我们有时就不必折腾的这么麻烦了,大概是作者不想破坏它功能上的简洁以及避免滥用导致不良的后果吧。


chemzqm
2k 声望83 粉丝

Javascript全栈开发,产品设计,自动化工具。追求简洁的设计,模块化开发,卓越的用户体验。